home *** CD-ROM | disk | FTP | other *** search
/ GFX Sensations 1 / Graphic Sensations - Volume 1.iso / tools / amiga / show / jpgagasr.lha / ppm2aga / ppm2ilbm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-05-29  |  26.5 KB  |  893 lines

  1. /* ppm2ilbm module                 */
  2. /* written by Günther Röhrich      */
  3. /* this is version 1.3             */
  4.  
  5. /* this code is tested with Aztec C 5.2a and GNU C 2.5.8 */
  6. /* NOTE: you will need the newiff V37 or higher package */
  7. /* (V37 is available on Fish-disk 705) */
  8.  
  9.  
  10. #include <clib/exec_protos.h>
  11. #include <libraries/iffparse.h>
  12. #include <dos/dos.h>
  13. #include <iffp/ilbmapp.h>
  14. #include <iffp/packer.h>
  15. #include <stdio.h>
  16. #include <graphics/modeid.h>
  17.  
  18. #include "ppm2AGA.h"
  19.  
  20. #ifndef __GNUC__
  21. #include <pragmas/exec_pragmas.h>
  22. #include <pragmas/iffparse_pragmas.h>
  23. #endif
  24.  
  25.  
  26.  
  27. /* needed when compiling with newiff V37 package */
  28. /* (not needed with V39 or higher) */
  29.  
  30. #ifndef BMHDF_CMAPOK
  31. #define BMHDF_CMAPOK (1 << 7)
  32. #endif
  33.  
  34.  
  35. /* externals used by this module */
  36.  
  37.  
  38. extern int AbortCheck(void);
  39. extern void PLProgress(ULONG Value, ULONG MaxValue);
  40. extern volatile unsigned short MaxError, MaxErrorPos;
  41. extern void EncodeHAM(UBYTE *yorig, UBYTE *yham, char *ColorTable, 
  42.                        short NumColors, short xsize);
  43. extern int MapColorASM(colorhist_vector colormap, pixval r1, pixval g1, pixval b1, 
  44.              int NumColors);
  45. extern int ExactColor;
  46. extern int GfxEnable;
  47. extern int VGAenable;
  48. extern int jpegAGA;
  49. extern char *ILBMfile;
  50.  
  51. /* definitions for the display routines */
  52.  
  53. extern int InitDisplay(int cols, int rows, ULONG Mode, int NumPlanes);
  54. extern void SetDisplayColor(int ColorNumber, UBYTE r, UBYTE g, UBYTE b);
  55. extern void DisplayRow(char *array, int cols, int row);
  56. extern void CloseDisplay(void);
  57.  
  58. /* globals defined in this module */
  59.  
  60. #ifdef DEBUG_ASM
  61. int MapColor(colorhist_vector colormap, pixval r1, pixval g1, pixval b1, 
  62.              int NumColors);
  63. #endif
  64.  
  65. unsigned short Mult_Table[2*256];
  66. unsigned long  Mult_Table32[2*256];
  67. jmp_buf ErrorEnv;
  68. int cols, rows, rowcnt;
  69. pixel **pixels;
  70. pixel *pixrow;
  71. pixval maxval;
  72. int ppmformat;
  73. char ColorTable[64*3] = {0};
  74. int ColorRegMax; 
  75. unsigned short ConvertMode;
  76. char *ColorCache; /* a 256K cache for EncodeHAM() */
  77.  
  78. /* these are needed by Floyd-Steinberg-routines */
  79. long  *thisrerr;
  80. long  *nextrerr;
  81. long  *thisgerr;
  82. long  *nextgerr;
  83. long  *thisberr; 
  84. long  *nextberr;     
  85. extern int floyd; 
  86. int fs_direction = 1;
  87. int Convert4096;
  88. #define FS_SCALE 1024
  89.  
  90. /* locals defined in this module */
  91.  
  92. static FILE *fppm;
  93. static FILE *ColorMapFile;
  94. static struct IFFHandle *iff;
  95. static struct ILBMInfo ilbm;
  96. static int error;
  97. static LONG IFFError;
  98. static unsigned char *coded_rowbuf;
  99. static unsigned char *hamarray;
  100. static int returnvalue;
  101. static void ppmCleanUp(void);
  102. static int readall;
  103. static colorhist_vector chv, colormap;
  104. static BYTE *CompressBuffer;
  105. static void encode_row(UBYTE *row, int cols, int nPlanes);
  106. static int ColorShift, NumColors;
  107. static gray *pgmrow;
  108.  
  109. static int lumcompare(colorhist_vector ch1, colorhist_vector ch2)
  110. {
  111.  return (int)((double)PPM_LUMIN(ch1->color) - (double)PPM_LUMIN(ch2->color));
  112. }
  113.  
  114.  
  115. int ppm2ilbm(char *PPMfile, ULONG Mode, int NumPlanes, ULONG MaxMem)
  116. {
  117.  int i, colors;
  118.  pixel *pP;
  119.  unsigned short AbsMaxError = 0;
  120.  unsigned short MaxErrorXPos = 0;
  121.  unsigned short MaxErrorYPos = 0;
  122.  
  123.  /* initialize everything                            */
  124.  /* this is needed because we may call this function */
  125.  /* multiple times                                   */
  126.  returnvalue = 0;
  127.  fppm = NULL;
  128.  ColorMapFile = NULL;
  129.  readall = 0;
  130.  pixrow = NULL;
  131.  pixels = NULL;
  132.  hamarray = NULL; coded_rowbuf = NULL; ColorCache=NULL; 
  133.  memset(&ilbm, 0, sizeof(struct ILBMInfo)); 
  134.  chv = NULL;
  135.  colormap = NULL;
  136.  CompressBuffer = NULL;
  137.  pgmrow = NULL;
  138.  
  139.  
  140.  /* multiplication table is needed by assembler subroutines */
  141.  for(i=-255; i<256; i++)
  142.  {
  143.    Mult_Table[i+255] = i*i;
  144.    Mult_Table32[i+255] = i*i;
  145.  }
  146.  
  147.  
  148.  /* if we encounter an error we longjmp() to this location       */
  149.  /* the ppmCleanUp() function will free all resources  allocated */
  150.  /* in this module                                               */
  151.  
  152.  error = setjmp(ErrorEnv);
  153.  if(error != 0) 
  154.  {
  155.   ppmCleanUp();
  156.   return error;
  157.  }  
  158.  
  159.  
  160.  /* try to open the ppm file */
  161.  fppm = fopen(PPMfile, "r");
  162.  if(!fppm) pm_error("Could not open file %s\n", PPMfile);
  163.  
  164.  /* read the header of the ppm file */
  165.  ppm_readppminit(fppm, &cols, &rows, &maxval, &ppmformat); 
  166.  
  167.  #ifdef DEBUG
  168.  pm_message("Cols: %d, Rows: %d, MaxVal: %d, Format: %d\n",
  169.              cols, rows, maxval, ppmformat);
  170.  #else
  171.  pm_message("Cols: %d, Rows: %d, MaxVal: %d\n", cols, rows, maxval);
  172.  #endif
  173.  
  174.  /* Initialize Floyd-Steinberg error vectors. */
  175.  thisrerr = (long*) pm_allocrow( cols + 2, sizeof(long) );
  176.  nextrerr = (long*) pm_allocrow( cols + 2, sizeof(long) );
  177.  thisgerr = (long*) pm_allocrow( cols + 2, sizeof(long) );
  178.  nextgerr = (long*) pm_allocrow( cols + 2, sizeof(long) );
  179.  thisberr = (long*) pm_allocrow( cols + 2, sizeof(long) );
  180.  nextberr = (long*) pm_allocrow( cols + 2, sizeof(long) );
  181.  srandom( (int) ( time( 0 ) ^ getpid(0) ) );
  182.  for ( i = 0; i < cols + 2; ++i )
  183.  {
  184.    thisrerr[i] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
  185.    thisgerr[i] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
  186.    thisberr[i] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
  187.    /* (random errors in [-1 .. 1]) */ 
  188.  }
  189.  
  190.  /* try to open the output file */
  191.  
  192.  if(!jpegAGA)
  193.  {
  194.    if(!(ilbm.ParseInfo.iff = AllocIFF()))
  195.      pm_error("Could not do AllocIFF()");
  196.  
  197.    iff = ilbm.ParseInfo.iff;
  198.  
  199.    IFFError = openifile((struct ParseInfo *)&ilbm, (UBYTE *)ILBMfile, IFFF_WRITE);
  200.    if(IFFError) pm_error(""); /* openifile() will print error messages... */
  201.  }
  202.  
  203.  if(Mode == HAM8 || Mode == HAM6 || Mode == COLORMAP)
  204.  {
  205.    int Clustering;
  206.    if(Mode == HAM8)
  207.    {
  208.      NumColors = NumPlanes;
  209.      ColorRegMax  = 63;
  210.      ColorShift   = 2; /* we only have 256K colors */
  211.      NumPlanes    = 8;
  212.      ConvertMode  = 1; /* Needed by EncodeHAM() */
  213.      Clustering   = 0;
  214.      Convert4096  = 0; /* No 4096-FS conversion */
  215.     
  216.    }
  217.    else if(Mode == HAM6)
  218.    {
  219.      NumColors = NumPlanes;
  220.      ColorRegMax = 15; 
  221.      ColorShift  = 4; /* converting to 4096 colors will be done by FS dithering */
  222.      NumPlanes   = 6;
  223.      ConvertMode = 0; /* Needed by EncodeHAM() */
  224.      Clustering  = 0;
  225.      if(floyd)
  226.        Convert4096 = 1; /* Do 4096-FS conversion */
  227.    }
  228.    else /* COLORMAP */
  229.    { 
  230.      NumColors = 1 << NumPlanes;      
  231.      ColorRegMax = 255;
  232.      ColorShift = 0; /* we will handle full 16M colors */
  233.      if(ppmformat == PGM_FORMAT || ppmformat == RPGM_FORMAT || ExactColor == 1)
  234.      {
  235.        Clustering = 0;
  236.      }
  237.      else
  238.      {
  239.        Clustering = 2;
  240.      }
  241.      Convert4096 = 0; /* No 4096-FS conversion */
  242.    } 
  243.        
  244.    if( cols*rows*3 <= (int)MaxMem)
  245.    {     
  246.      /* read the complete picture into memory */
  247.      int row;
  248.      readall = 1;
  249.      pm_message("Reading complete picture into memory...\n");
  250.      pixels = ppm_allocarray(cols, rows);     
  251.      for(row = 0; row < rows; ++row)
  252.      {
  253.        if(AbortCheck()) pm_error("^C\n");
  254.        ppm_readppmrow(fppm, pixels[row], cols, maxval, ppmformat);
  255.        if(maxval != ColorRegMax)
  256.        {
  257.          if(maxval == 255)
  258.          {
  259.            for(i=0, pP=pixels[row]; i < cols; ++i, ++pP)
  260.            {
  261.              pP->r = pP->r >> ColorShift;
  262.              pP->g = pP->g >> ColorShift;
  263.              pP->b = pP->b >> ColorShift;
  264.            }
  265.          }
  266.          else
  267.          {
  268.            for(i=0, pP=pixels[row]; i < cols; ++i, ++pP)
  269.              PPM_DEPTH(*pP, *pP, maxval, ColorRegMax);
  270.          }
  271.        }
  272.      }
  273.    }
  274.    else
  275.    {
  276.      rowcnt = 0;
  277.      if(!(pixrow = ppm_allocrow(cols)))
  278.        pm_error("Out of memory.\n");     
  279.     }
  280.  
  281.  
  282.    /* allocate enough room for one row of chunky HAM pixels  */
  283.    /* (this array will also be used for colormap conversion) */
  284.    if(!(hamarray = malloc(RowBits(cols))))
  285.      pm_error("Out of memory.\n");
  286.  
  287.    /* we only have to clear the space between cols and RowBits(cols)    */
  288.    /* hamarray will be larger in most cases because of special alignment*/
  289.    for(i = cols; i < RowBits(cols); i++) hamarray[i] = 0;
  290.    pm_message("Computing colormap...\n");
  291.  
  292.  
  293.    /* now we make a histogram of the picture...          */
  294.    /* if we increase Clustering we will find less colors */
  295.    /* we allow a maximum of 10000 colors                 */
  296.    /* ColorShift is the shift needed for HAM encoding    */
  297.    for(i=Clustering; i<5; i++)
  298.    {
  299.      if((chv = ppm_fcomputecolorhist(fppm, cols, rows, 10000, &colors, ColorShift, i)))
  300.        break;
  301.      if(AbortCheck()) pm_error("^C\n");
  302.      pm_message("Could not compute colormap, trying again...\n");
  303.      rowcnt=0;
  304.      if(!readall)
  305.      { 
  306.        rewind(fppm); /* re-initialise the ppm pointers for reading again */
  307.        ppm_readppminit(fppm, &cols, &rows, &maxval, &ppmformat);       
  308.      }
  309.    } 
  310.  
  311.    pm_message("Found %d colors.\n", colors);
  312.  
  313.  
  314.    /* now we try to find a suitable colormap... */
  315.    colormap = mediancut(chv, colors, rows*cols, ColorRegMax, NumColors);
  316.    ppm_freecolorhist(chv); /* we don't need the histogram any more */
  317.    chv = NULL;
  318.    /* sort the colors in ascending luminance order...  */ 
  319.    /* (the first color will be the background color)      */
  320.    if(NumColors < colors)
  321.    {
  322.      qsort((char *)colormap, NumColors, sizeof(struct colorhist_item), lumcompare);
  323.    }
  324.    else
  325.    {
  326.      qsort((char *)colormap, colors, sizeof(struct colorhist_item), lumcompare);
  327.    }
  328.  
  329.    
  330.    if(!jpegAGA)
  331.    {   
  332.      /* the CompressBuffer is needed by the cmpByteRun1 compressor */
  333.      if(!(CompressBuffer = malloc(MaxPackedSize(RowBytes(cols)))))
  334.        pm_error("Out of memory allocating the compress buffer.\n");
  335.  
  336.      /* the ColorCache is needed by EncodeHAM() for higher speed */
  337.      /* (for HAM6 encoding only 4097 bytes are needed...)        */
  338.      if(!(ColorCache = malloc(262145)))
  339.        pm_error("Out of memory allocating the color cache.\n");
  340.  
  341.      /* the coded_rowbuf is needed by the IFF encoder */
  342.      if(!(coded_rowbuf = (unsigned char *)malloc(RowBytes(cols))))
  343.        pm_error("Out of memory allocating coded_rowbuf.\n");
  344.      for(i=RowBytes(cols)-1; i>=0; i--) coded_rowbuf[i]=0;
  345.  
  346.      /* write the FORM chunk */
  347.      IFFError = PushChunk(iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN);
  348.      if(IFFError) pm_error("Error writing FORM chunk.\n");
  349.      
  350.      /* initialize the BMHD chunk */
  351.      ilbm.Bmhd.w = cols;
  352.      ilbm.Bmhd.h = rows;             /* Width, height in pixels */
  353.      ilbm.Bmhd.x = 0;
  354.      ilbm.Bmhd.y = 0;                /* x, y position for this bitmap  */
  355.      ilbm.Bmhd.nPlanes = NumPlanes;  /* # of planes (not including mask) */
  356.      ilbm.Bmhd.masking = mskNone;   
  357.      ilbm.Bmhd.compression = cmpByteRun1;   
  358.      ilbm.Bmhd.flags = BMHDF_CMAPOK;        /* CMAP has 8 significant bits */
  359.      ilbm.Bmhd.transparentColor = 0; 
  360.      ilbm.Bmhd.xAspect = cols;         
  361.      ilbm.Bmhd.yAspect = rows;       /* xAspect, yAspect */
  362.      ilbm.Bmhd.pageWidth = cols;
  363.      ilbm.Bmhd.pageHeight = rows;    /* pageWidth, pageHeight */
  364.   
  365.      /* write the BMHD chunk */   
  366.      IFFError = putbmhd(iff, &ilbm.Bmhd);
  367.      if(IFFError) pm_error("Error writing BMHD chunk.\n");
  368.   
  369.      if(GfxEnable) InitDisplay(cols, rows, Mode, NumPlanes);
  370.    }
  371.    
  372.    if(Mode == HAM8 || Mode == HAM6)
  373.    { 
  374.      /* fill in the ColorTable in B R G order */
  375.      /* (this is better for HAM encoding)     */       
  376.      for(i=0; i<NumColors; i++)
  377.      {
  378.        ColorTable[i*3]   = PPM_GETB(colormap[i].color);
  379.        ColorTable[i*3+1] = PPM_GETR(colormap[i].color);
  380.        ColorTable[i*3+2] = PPM_GETG(colormap[i].color);
  381.        if(!jpegAGA)
  382.          SetDisplayColor(i, PPM_GETR(colormap[i].color) << ColorShift,
  383.                             PPM_GETG(colormap[i].color) << ColorShift,
  384.                             PPM_GETB(colormap[i].color) << ColorShift);
  385.      }
  386.      free(colormap); /* we don't need the colormap any more */
  387.      colormap = NULL;
  388.  
  389.      if(jpegAGA)
  390.      {
  391.        unsigned short MagicNumber = 0x1203; /* guess what this means! */
  392.        int Reserved=0; 
  393.        unsigned char color;
  394.   
  395.        if(!(ColorMapFile = fopen(ILBMfile, "w")))
  396.          pm_error("Could not open color map file for jpegAGA.\n");
  397.        if(fwrite(&MagicNumber, 2, 1, ColorMapFile) != 1) pm_error("Write error.\n");
  398.        if(fwrite(&Reserved,    4, 1, ColorMapFile) != 1) pm_error("Write error.\n");     
  399.        if(fwrite(ColorTable,  64*3, 1, ColorMapFile) != 1) pm_error("Write error.\n");
  400.        pm_message("Colormap file for jpegAGA created.\n");
  401.        ppmCleanUp();
  402.        return 0;
  403.      }
  404.  
  405.      memset(ColorCache, 0, 262145); /* clear the color cache */          
  406.  
  407.      do
  408.      {
  409.        int y;
  410.        rowcnt=0;
  411.        
  412.        /* if we don't need an additional color the color cache */
  413.        /* will remain valid and should not be cleared          */
  414.  
  415.        memset(ColorCache, 0, 262145); /* clear the color cache */
  416.    
  417.  
  418.        if(!readall)
  419.        {
  420.          rewind(fppm);
  421.          ppm_readppminit(fppm, &cols, &rows, &maxval, &ppmformat);
  422.        }
  423.  
  424.        /* if we have all colors we can write the CMAP chunk */
  425.        if(NumColors == ColorRegMax+1)
  426.        {
  427.          IFFError = PushChunk(iff, 0, ID_CMAP, (ColorRegMax+1)*3);
  428.          if(IFFError) pm_error("Error writing CMAP header.\n");
  429.  
  430.          for( i = 0; i <= ColorRegMax; i++) 
  431.          {
  432.            ColorRegister cmapReg;
  433.            cmapReg.red   = ColorTable[i*3+1] << ColorShift;   /* red */
  434.            cmapReg.green = ColorTable[i*3+2] << ColorShift;   /* green */
  435.            cmapReg.blue  = ColorTable[i*3]   << ColorShift;   /* blue */
  436.            IFFError = WriteChunkBytes(iff, (UBYTE *)&cmapReg, 3);
  437.            if(IFFError!=3) pm_error("Error writing CMAP chunk.\n");
  438.          }
  439.          IFFError = PopChunk(iff); /* close CMAP */
  440.          if(IFFError) pm_error("Error closing CMAP.\n");
  441.  
  442.          /* write the CAMG chunk */
  443.          if(VGAenable)
  444.          {
  445.            if(cols > 370 || rows > 260)
  446.            {
  447.              ilbm.camg = VGAPRODUCTHAM_KEY;
  448.            }
  449.            else
  450.            {
  451.              ilbm.camg = VGALORESHAMDBL_KEY;
  452.            }
  453.          }
  454.          else
  455.          {
  456.            ilbm.camg = HAM;
  457.            if(cols > 370) ilbm.camg = ilbm.camg | HIRES;
  458.            if(rows > 280) ilbm.camg = ilbm.camg | LACE;
  459.          }
  460.          IFFError = putcamg(iff, &ilbm.camg);
  461.          if(IFFError) pm_error("Error writing CAMG chunk.\n");
  462.  
  463.          /* start with the BODY chunk */
  464.          IFFError = PushChunk(iff, 0, ID_BODY, IFFSIZE_UNKNOWN);
  465.          if(IFFError) pm_error("Error writing BODY header.\n");
  466.        }
  467.  
  468.        /* set a dummy value for AbsMaxError                          */
  469.        /* it will contain the maximum error for the complete picture */ 
  470.        AbsMaxError=0;
  471.   
  472.        for(y=0; y<rows; y++)
  473.        {
  474.          pixel *nextrow;          
  475.  
  476.          MaxError = 0;         
  477.            
  478.          nextrow = next_pixrow(fppm, y, ColorShift);
  479.           
  480.          if(AbortCheck()) pm_error("^C\n");
  481.  
  482.          EncodeHAM(nextrow, hamarray, ColorTable, NumColors*3, (short)cols);
  483.  
  484.          
  485.          /* remember the color of the pixel with the maximum error */
  486.          /* even if MaxError == 0 we will get a valid entry in the */
  487.          /* colormap but it will not be used by the picture        */           
  488.          if(MaxError > AbsMaxError)
  489.          {
  490.            MaxErrorXPos = MaxErrorPos;
  491.            MaxErrorYPos = y;
  492.            AbsMaxError = MaxError;
  493.  
  494.            /* use the color of the pixel with the maximum error as */
  495.            /* a ColorTable entry                                   */
  496.            /* we will have to recompute the picture if we want to  */
  497.            /* use the additional color                             */
  498.            if(NumColors <= ColorRegMax)
  499.            {
  500.              ColorTable[NumColors*3]   = PPM_GETB(pixrow[MaxErrorXPos]);
  501.              ColorTable[NumColors*3+1] = PPM_GETR(pixrow[MaxErrorXPos]);
  502.              ColorTable[NumColors*3+2] = PPM_GETG(pixrow[MaxErrorXPos]);
  503.            }        
  504.          }
  505.  
  506.          /* if all colors are set we can write our row to the BODY chunk */
  507.          if(NumColors == ColorRegMax+1) 
  508.          {
  509.            encode_row(hamarray, cols, NumPlanes);
  510.          }
  511.          DisplayRow(hamarray, cols, y);
  512.        }
  513.  
  514.        /* this is only for debugging purposes */
  515.        if(NumColors <= ColorRegMax)
  516.        {
  517.          #ifdef DEBUG
  518.          pm_message("Max Error: %hd ", AbsMaxError);
  519.          #endif
  520.          if(AbsMaxError == 0)
  521.          {
  522.            printf("\n");
  523.          }
  524.          else
  525.          {
  526.            #ifdef DEBUG
  527.            pm_message("Color %hd, XPos: %hd, YPos: %hd\n", NumColors, MaxErrorXPos, MaxErrorYPos);
  528.            #endif
  529.          }
  530.        }
  531.        SetDisplayColor(NumColors, ColorTable[NumColors*3+1] << ColorShift,
  532.                                   ColorTable[NumColors*3+2] << ColorShift,
  533.                                   ColorTable[NumColors*3]   << ColorShift);
  534.        NumColors = NumColors + 1; 
  535.     
  536.      /* finish if we have set all colors */
  537.      } while(NumColors <= ColorRegMax+1);
  538.  
  539.  
  540.      #ifdef DEBUG
  541.      pm_message("Max Error: %hd, XOffset: %hd YOffset: %hd\n",
  542.                AbsMaxError, MaxErrorXPos, MaxErrorYPos);
  543.  
  544.      pm_message("Colortable: ");
  545.      for(i=0; i<=ColorRegMax; i++)
  546.      {
  547.        pm_message("%hd,%hd,%hd ",ColorTable[i*3],
  548.                    ColorTable[i*3+1],ColorTable[i*3+2]);
  549.      }
  550.      pm_message("\n");
  551.      #endif
  552.  
  553.      /* close the BODY chunk */
  554.      IFFError = PopChunk(iff); 
  555.      if(IFFError) pm_message("Error closing the BODY chunk.\n");
  556.    }
  557.    
  558.  
  559.    else if(Mode == COLORMAP)
  560.    { 
  561.      /* if floyd is nonzero we will use Floyd-Steinberg dithering */
  562.      int row, fs_direction;
  563.      register int col, limitcol, ind;
  564.      register pixel *pP;
  565.      register long sg, sr, sb, err;
  566.      long *temperr;
  567.  
  568.      /*
  569.      ** map the colors in the image to their closest match in the
  570.      ** new colormap, and write 'em out.
  571.      */
  572.        
  573.      pm_message( "Mapping image to new colors...\n" );
  574.  
  575.      /* write the CMAP chunk */
  576.      IFFError = PushChunk(iff, 0, ID_CMAP, NumColors*3);
  577.      if(IFFError) pm_error("Error writing CMAP header.\n");
  578.      for( i = 0; i < NumColors; i++) 
  579.      {
  580.        ColorRegister cmapReg;
  581.        cmapReg.red   = PPM_GETR( colormap[i].color );   /* red   */
  582.        cmapReg.green = PPM_GETG( colormap[i].color );   /* green */
  583.        cmapReg.blue  = PPM_GETB( colormap[i].color );   /* blue  */
  584.        SetDisplayColor(i, cmapReg.red, cmapReg.green, cmapReg.blue);
  585.        IFFError = WriteChunkBytes(iff, (UBYTE *)&cmapReg, 3);
  586.        if(IFFError!=3) pm_error("Error writing CMAP chunk.\n");
  587.      }
  588.      IFFError = PopChunk(iff); /* close CMAP */
  589.      if(IFFError) pm_error("Error closing CMAP.\n");
  590.  
  591.      /* write the CAMG chunk */
  592.      ilbm.camg = 0;
  593.      if(VGAenable)
  594.      {
  595.        if(cols > 370 || rows > 260)
  596.          ilbm.camg = VGAPRODUCT_KEY;
  597.        else
  598.          ilbm.camg = VGALORESDBL_KEY;
  599.      }
  600.      else
  601.      {
  602.        if(cols > 370) ilbm.camg = ilbm.camg | HIRES;
  603.        if(rows > 280) ilbm.camg = ilbm.camg | LACE;
  604.      }
  605.      IFFError = putcamg(iff, &ilbm.camg);
  606.      if(IFFError) pm_error("Error writing CAMG chunk.\n");
  607.  
  608.      /* start with the BODY chunk */
  609.      IFFError = PushChunk(iff, 0, ID_BODY, IFFSIZE_UNKNOWN);
  610.      if(IFFError) pm_error("Error writing BODY header.\n");
  611.  
  612.      rowcnt=0;
  613.      if(!readall)
  614.      {
  615.        rewind(fppm);
  616.        ppm_readppminit(fppm, &cols, &rows, &maxval, &ppmformat);       
  617.      }
  618.  
  619.      if ( floyd )
  620.      {
  621.        fs_direction = 1;
  622.      }
  623.      for ( row = 0; row < rows; ++row )
  624.      {
  625.        /* PLProgress(row, rows); */
  626.        if(AbortCheck()) pm_error("^C\n");
  627.        if ( floyd )
  628.          for ( col = 0; col < cols + 2; ++col )
  629.            nextrerr[col] = nextgerr[col] = nextberr[col] = 0;
  630.        if ( ( ! floyd ) || fs_direction )
  631.        {
  632.          col = 0;
  633.          limitcol = cols;
  634.          /* pP = pixels[row]; */
  635.          pP = next_pixrow(fppm, row, ColorShift);
  636.        }
  637.        else
  638.        {
  639.          col = cols - 1;
  640.          limitcol = -1;
  641.          /* pP = &(pixels[row][col]); */
  642.          pP = &(next_pixrow(fppm, row, ColorShift)[col]);
  643.        }
  644.        do
  645.        {
  646.          if ( floyd )
  647.          {
  648.            /* Use Floyd-Steinberg errors to adjust actual color. */
  649.            sr = PPM_GETR(*pP) + thisrerr[col + 1] / FS_SCALE;
  650.            sg = PPM_GETG(*pP) + thisgerr[col + 1] / FS_SCALE;
  651.            sb = PPM_GETB(*pP) + thisberr[col + 1] / FS_SCALE;
  652.            if ( sr < 0 ) sr = 0;
  653.            else if ( sr > maxval ) sr = maxval;
  654.            if ( sg < 0 ) sg = 0;
  655.            else if ( sg > maxval ) sg = maxval;
  656.            if ( sb < 0 ) sb = 0;
  657.            else if ( sb > maxval ) sb = maxval;
  658.            PPM_ASSIGN( *pP, sr, sg, sb );
  659.          }
  660.  
  661.          /* use the assembler subroutine for the colormap search */
  662.          /* this will consume most of the CPU time               */                     
  663.          ind = MapColorASM(colormap, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP),
  664.                            NumColors);
  665.            
  666.          /* compare the results of the ASM versus the C routine */
  667.          /* if they differ the ASM routine has a bug...         */
  668.          /* (we don't need them because they are the same now)  */
  669.          #ifdef DEBUG_ASM
  670.          if(ind != MapColor(colormap, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP),
  671.                             NumColors))
  672.            pm_message("Diff: row %d col %d  r %d g %d b %d\n", row,col,
  673.                        (int)PPM_GETR(*pP), (int)PPM_GETG(*pP), (int)PPM_GETB(*pP));
  674.          #endif
  675.  
  676.          if ( floyd )
  677.          {
  678.            /* Propagate Floyd-Steinberg error terms. */
  679.            if ( fs_direction )
  680.            {
  681.              err = ( sr - (long) PPM_GETR( colormap[ind].color ) ) * FS_SCALE;
  682.              thisrerr[col + 2] += ( err * 7 ) / 16;
  683.              nextrerr[col    ] += ( err * 3 ) / 16;
  684.              nextrerr[col + 1] += ( err * 5 ) / 16;
  685.              nextrerr[col + 2] += ( err     ) / 16;
  686.              err = ( sg - (long) PPM_GETG( colormap[ind].color ) ) * FS_SCALE;
  687.              thisgerr[col + 2] += ( err * 7 ) / 16;
  688.              nextgerr[col    ] += ( err * 3 ) / 16;
  689.              nextgerr[col + 1] += ( err * 5 ) / 16;
  690.              nextgerr[col + 2] += ( err     ) / 16;
  691.              err = ( sb - (long) PPM_GETB( colormap[ind].color ) ) * FS_SCALE;
  692.              thisberr[col + 2] += ( err * 7 ) / 16;
  693.              nextberr[col    ] += ( err * 3 ) / 16;
  694.              nextberr[col + 1] += ( err * 5 ) / 16;
  695.              nextberr[col + 2] += ( err     ) / 16;
  696.            }
  697.            else
  698.            {
  699.              err = ( sr - (long) PPM_GETR( colormap[ind].color ) ) * FS_SCALE;
  700.              thisrerr[col    ] += ( err * 7 ) / 16;
  701.              nextrerr[col + 2] += ( err * 3 ) / 16;
  702.              nextrerr[col + 1] += ( err * 5 ) / 16;
  703.              nextrerr[col    ] += ( err     ) / 16;
  704.              err = ( sg - (long) PPM_GETG( colormap[ind].color ) ) * FS_SCALE;
  705.              thisgerr[col    ] += ( err * 7 ) / 16;
  706.              nextgerr[col + 2] += ( err * 3 ) / 16;
  707.              nextgerr[col + 1] += ( err * 5 ) / 16;
  708.              nextgerr[col    ] += ( err     ) / 16;
  709.              err = ( sb - (long) PPM_GETB( colormap[ind].color ) ) * FS_SCALE;
  710.              thisberr[col    ] += ( err * 7 ) / 16;
  711.              nextberr[col + 2] += ( err * 3 ) / 16;
  712.              nextberr[col + 1] += ( err * 5 ) / 16;
  713.              nextberr[col    ] += ( err     ) / 16;
  714.            }
  715.          }
  716.  
  717.          /* *pP = colormap[ind].color; */
  718.          hamarray[col] = (unsigned char)ind;
  719.  
  720.          if ( ( ! floyd ) || fs_direction )
  721.          {
  722.            ++col;
  723.            ++pP;
  724.          }
  725.          else
  726.          {
  727.            --col;
  728.            --pP;
  729.          }
  730.        }
  731.        while ( col != limitcol );
  732.  
  733.        if ( floyd )
  734.        {
  735.          temperr = thisrerr;
  736.          thisrerr = nextrerr;
  737.          nextrerr = temperr;
  738.          temperr = thisgerr;
  739.          thisgerr = nextgerr;
  740.          nextgerr = temperr;
  741.          temperr = thisberr;
  742.          thisberr = nextberr;
  743.          nextberr = temperr;
  744.          fs_direction = ! fs_direction;
  745.        }
  746.  
  747.        /* ppm_writeppmrow( stdout, pixels[row], cols, maxval, 0 ); */
  748.        /* write one row to the BODY chunk */
  749.        encode_row(hamarray, cols, NumPlanes);
  750.        DisplayRow(hamarray, cols, row);
  751.      }
  752.      IFFError = PopChunk(iff); /* close the BODY */
  753.      if(IFFError) pm_error("Error closing the BODY chunk.\n");
  754.    }       
  755.  }
  756.  
  757.  closeifile((struct ParseInfo *)&ilbm);
  758.  ppmCleanUp();
  759.  return 0;
  760. }
  761.  
  762.  
  763. /* free all resources used by this module */
  764. void ppmCleanUp(void)
  765. {
  766.  CloseDisplay();
  767.  if(colormap) free(colormap);
  768.  if(pgmrow) pgm_freerow(pgmrow); 
  769.  if(coded_rowbuf) free(coded_rowbuf);
  770.  if(CompressBuffer) free(CompressBuffer);
  771.  if(ColorCache) free(ColorCache);
  772.  if(chv) free(chv);
  773.  if(hamarray) free(hamarray);
  774.  if(pixels)
  775.  {
  776.    ppm_freearray(pixels, rows);
  777.  } 
  778.  else
  779.  {
  780.    if(pixrow) pbm_freerow(pixrow);
  781.  }
  782.  if(fppm) fclose(fppm);
  783.  
  784.  if(jpegAGA)
  785.  {
  786.    if(ColorMapFile)
  787.    {
  788.      fclose(ColorMapFile);
  789.      ColorMapFile = NULL;
  790.    }
  791.  }
  792.  else
  793.  {
  794.    if(ilbm.ParseInfo.opened)
  795.    {
  796.      closeifile((struct ParseInfo *)&ilbm);
  797.      remove(ILBMfile);
  798.    }
  799.    if(ilbm.ParseInfo.iff) FreeIFF(ilbm.ParseInfo.iff);
  800.  }
  801. }
  802.  
  803.  
  804.  
  805. /* convert one row from chunky to planar, compress it, */
  806. /* and write it to the BODY chunk                      */
  807.  
  808. /* encode algorithm by Johan Widen (jw@jwdata.se) */
  809. /* modified by Günther Röhrich */
  810.  
  811. const unsigned char bit_mask[] = {1, 2, 4, 8, 16, 32, 64, 128};
  812.  
  813. static void
  814. encode_row(UBYTE *row, int cols, int nPlanes)
  815. {
  816.  register int plane, col;
  817.  int bytes;
  818.  LONG packedRowBytes;
  819.  BYTE *source;
  820.  BYTE *destination;
  821.  
  822.  bytes = RowBytes(cols);
  823.  
  824.  /* Encode and write raw bytes in plane-interleaved form. */
  825.  for( plane = 0; plane < nPlanes; plane++ ) 
  826.  {
  827.    int mask, cbit, wr;
  828.    unsigned char *cp;
  829.    UBYTE *rp;
  830.  
  831.    mask = 1 << plane;
  832.    cbit = -1;
  833.    cp = coded_rowbuf-1;
  834.    rp = row;
  835.    for( col = 0; col < cols; col++, cbit--, rp++ ) 
  836.    {
  837.      if( cbit < 0 ) 
  838.      {
  839.        cbit = 7;
  840.        *++cp = 0;
  841.      }
  842.      if( *rp & mask )
  843.        *cp |= bit_mask[cbit];
  844.    }
  845.    /* wr = fwrite(coded_rowbuf, 1, bytes, stdout); */
  846.  
  847.    /* compress the row */
  848.    source = coded_rowbuf;
  849.    destination = CompressBuffer;
  850.    packedRowBytes = packrow(&source, &destination, bytes);
  851.  
  852.    /* write the compressed row to the BODY chunk */  
  853.    IFFError = WriteChunkBytes(iff, CompressBuffer, packedRowBytes); 
  854.    if(IFFError!=packedRowBytes) pm_error("Error writing BODY chunk.\n");
  855.  }
  856. }
  857.  
  858.  
  859. #ifdef DEBUG_ASM
  860. /* this is the C version of the colormap search routine */
  861. /* (it is now replaced by the ASM version)              */
  862.  
  863. /* search colormap for closest match. */
  864. int MapColor(colorhist_vector colormap, pixval r, pixval g, pixval b, 
  865.              int newcolors)
  866.  int ind;
  867.  register int i, r1, g1, b1, r2, g2, b2;
  868.  register long dist, newdist;
  869.  r1 = r;
  870.  g1 = g;
  871.  b1 = b;
  872.  dist = 2000000000;
  873.  for ( i = 0; i < newcolors; ++i )
  874.  {
  875.    r2 = PPM_GETR( colormap[i].color );
  876.    g2 = PPM_GETG( colormap[i].color );
  877.    b2 = PPM_GETB( colormap[i].color );
  878.    newdist = ( r1 - r2 ) * ( r1 - r2 ) +
  879.              ( g1 - g2 ) * ( g1 - g2 ) +
  880.              ( b1 - b2 ) * ( b1 - b2 );
  881.    if ( newdist < dist )
  882.    {
  883.      ind = i;
  884.      dist = newdist;
  885.    }
  886.  }
  887.  return ind;
  888. }
  889. #endif
  890.  
  891.  
  892.